/*
 * Copyright 2002-2023 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.format.support;

import java.lang.annotation.Annotation;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.core.DecoratingProxy;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter;
import org.springframework.core.convert.converter.GenericConverter;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.format.AnnotationFormatterFactory;
import org.springframework.format.Formatter;
import org.springframework.format.FormatterRegistry;
import org.springframework.format.Parser;
import org.springframework.format.Printer;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.StringValueResolver;

/**
 * FormattingConversionService 主要是由字符串 到特定格式对象转换 已经 其他格式到字符串转换的工具类，同时支持 ConversionService 接口的功能。
 *
 * FormattingConversionService 是 Spring 框架中一个具体的 ConversionService 实现，它不仅提供了标准的类型转换功能，还特别增强了对格式化和解析的支持。
 * 这意味着它可以处理复杂的格式化需求，如日期、数字、货币等，并且可以与国际化（i18n）紧密结合。
 * 下面是对 FormattingConversionService 与其他组件（如 ConverterRegistry、Parser、Printer、Converter 和 GenericConverter）的详细比较分析。
 *
 * 1. FormattingConversionService
 * 	功能
 * 		增强的格式化支持：FormattingConversionService 继承自 DefaultConversionService，并添加了对格式化和解析的支持。它内置了许多常见的格式化器（Formatter），如 DateFormatter、NumberFormatter 等，可以直接用于处理特定类型的格式化需求。
 * 		国际化支持：它能够根据当前的 Locale 自动调整格式化规则，确保应用程序在不同语言环境下都能正确地显示数据。
 * 		集成 Formatter 接口：除了传统的 Converter 和 GenericConverter，FormattingConversionService 还支持 Formatter<T> 接口，该接口同时包含了 Parser<T> 和 Printer<T> 的功能，提供了一种统一的方式来处理对象和字符串之间的转换。
 *
 *
 * 2.使用场景
 * 		复杂的数据格式化：当需要处理日期、时间、数字、货币等复杂的数据格式时，FormattingConversionService 提供了内置的支持，减少了开发者的工作量。
 * 		国际化应用：对于需要支持多语言的 Web 应用程序来说，FormattingConversionService 可以确保所有输出都按照用户的语言环境进行适当的格式化。
 * 		表单提交和验证：在处理表单提交时，FormattingConversionService 可以自动将用户输入的字符串转换为相应的 Java 类型，并且可以在必要时应用格式化规则。
 *
 * 3. 与 Converter 和 GenericConverter 的关系
 * 		扩展性：虽然 FormattingConversionService 主要关注格式化和解析，但它仍然支持 Converter<S, T> 和 GenericConverter，使得它可以处理更广泛的类型转换需求。
 * 		对于那些不需要复杂格式化的简单转换任务，Converter 和 GenericConverter 仍然是有效的选择。
 *
 * <br>
 * A {@link org.springframework.core.convert.ConversionService} implementation
 * designed to be configured as a {@link FormatterRegistry}.
 * 实现 ConversionService 和 FormatterRegistry，
 * 用于将字段类型与格式化器（Printer 和 Parser）关联起来， 一般是先把 需要打印的对象 转换为字符串（如果需要的化），然后格式化打印
 *
 * @author Keith Donald
 * @author Juergen Hoeller
 * @since 3.0
 */
public class FormattingConversionService extends GenericConversionService
		implements FormatterRegistry, EmbeddedValueResolverAware {

	@Nullable
	private StringValueResolver embeddedValueResolver;

	/**
	 * 缓存注解转换器键，用于快速查找已注册的转换器
	 */
	private final Map<AnnotationConverterKey, GenericConverter> cachedPrinters = new ConcurrentHashMap<>(64);

	private final Map<AnnotationConverterKey, GenericConverter> cachedParsers = new ConcurrentHashMap<>(64);


	@Override
	public void setEmbeddedValueResolver(StringValueResolver resolver) {
		this.embeddedValueResolver = resolver;
	}

	/**
	 * 添加打印器
	 * @param printer the printer to add
	 */
	@Override
	public void addPrinter(Printer<?> printer) {
		//1、获取 ？代表的实际Class类型
		Class<?> fieldType = getFieldType(printer, Printer.class);
		//2、添加转换器，将特定类型 转换为字符串类型
		addConverter(new PrinterConverter(fieldType, printer, this));
	}

	/**
	 * 添加一个 解析器（由字符串 解析成特定对象）
	 * @param parser the parser to add
	 */
	@Override
	public void addParser(Parser<?> parser) {
		//1、获取 ？代表的实际Class类型
		Class<?> fieldType = getFieldType(parser, Parser.class);
		//2、添加转换器，将字符串 转换为特定对象类型
		addConverter(new ParserConverter(fieldType, parser, this));
	}

	/**
	 * 添加格式化器（打印器和解析器的组合）
	 * @param formatter the formatter to add
	 */
	@Override
	public void addFormatter(Formatter<?> formatter) {
		addFormatterForFieldType(getFieldType(formatter), formatter);
	}

	@Override
	public void addFormatterForFieldType(Class<?> fieldType, Formatter<?> formatter) {
		//1、添加转换器，将特定类型 转换为字符串
		addConverter(new PrinterConverter(fieldType, formatter, this));
		//2、添加转换器，将字符串 转换为特定对象类型
		addConverter(new ParserConverter(fieldType, formatter, this));
	}

	@Override
	public void addFormatterForFieldType(Class<?> fieldType, Printer<?> printer, Parser<?> parser) {
		addConverter(new PrinterConverter(fieldType, printer, this));
		addConverter(new ParserConverter(fieldType, parser, this));
	}

	@Override
	public void addFormatterForFieldAnnotation(AnnotationFormatterFactory<? extends Annotation> annotationFormatterFactory) {
		Class<? extends Annotation> annotationType = getAnnotationType(annotationFormatterFactory);
		if (this.embeddedValueResolver != null &&
				annotationFormatterFactory instanceof EmbeddedValueResolverAware embeddedValueResolverAware) {
			embeddedValueResolverAware.setEmbeddedValueResolver(this.embeddedValueResolver);
		}
		Set<Class<?>> fieldTypes = annotationFormatterFactory.getFieldTypes();
		for (Class<?> fieldType : fieldTypes) {
			addConverter(new AnnotationPrinterConverter(annotationType, annotationFormatterFactory, fieldType));
			addConverter(new AnnotationParserConverter(annotationType, annotationFormatterFactory, fieldType));
		}
	}


	static Class<?> getFieldType(Formatter<?> formatter) {
		return getFieldType(formatter, Formatter.class);
	}

	/**
	 *
	 *
	 * 获取泛型接口的字段类型 比如：Printer<Date> 那么这里的 fieldType 就是 Date.class
	 * @param instance the instance to introspect，比如 Printer<Date> 那么这里的 instance 就是 Printer<Date> 实例
	 * @param genericInterface the generic interface to extract the parameterized type from，比如 Printer.class 或者 Parser.class
	 * @return
	 * @param <T>
	 */
	private static <T> Class<?> getFieldType(T instance, Class<T> genericInterface) {
		//1、获取泛型接口的字段类型 比如：Printer<Date> 那么这里的 fieldType 就是 Date.class
		Class<?> fieldType = GenericTypeResolver.resolveTypeArgument(instance.getClass(), genericInterface);
		if (fieldType == null && instance instanceof DecoratingProxy decoratingProxy) {
			//2、如果fieldType 为空，并且 instance 是 DecoratingProxy 类型，那么就获取装饰类的字段类型 ，
			fieldType = GenericTypeResolver.resolveTypeArgument(decoratingProxy.getDecoratedClass(), genericInterface);
		}
		Assert.notNull(fieldType, () -> "Unable to extract the parameterized field type from " +
					ClassUtils.getShortName(genericInterface) + " [" + instance.getClass().getName() +
					"]; does the class parameterize the <T> generic type?");
		return fieldType;
	}

	@SuppressWarnings("unchecked")
	static Class<? extends Annotation> getAnnotationType(AnnotationFormatterFactory<? extends Annotation> factory) {
		Class<? extends Annotation> annotationType = (Class<? extends Annotation>)
				GenericTypeResolver.resolveTypeArgument(factory.getClass(), AnnotationFormatterFactory.class);
		if (annotationType == null) {
			throw new IllegalArgumentException("Unable to extract parameterized Annotation type argument from " +
					"AnnotationFormatterFactory [" + factory.getClass().getName() +
					"]; does the factory parameterize the <A extends Annotation> generic type?");
		}
		return annotationType;
	}


	/**
	 * 打印转换器，先转换 再打印
	 */
	private static class PrinterConverter implements GenericConverter {

		/**
		 * 被打印的类型
		 */
		private final Class<?> fieldType;

		/**
		 * Printer 对象类型，也就是 Printer<?> 中的泛型类型 T 比如：Printer<Date> 那么这里的 printerObjectType 就是 Date.class
		 */
		private final TypeDescriptor printerObjectType;

		/**
		 * 打印器
		 */
		@SuppressWarnings("rawtypes")
		private final Printer printer;

		private final ConversionService conversionService;

		/**
		 *
		 * @param fieldType Formatter 实例对象类型 （源实例对象类型）
		 * @param printer	实现 Printer接口的 实例对象（目标对对象）
		 * @param conversionService 核心转换服务
		 */
		public PrinterConverter(Class<?> fieldType, Printer<?> printer, ConversionService conversionService) {
			//1、源实例对象类型
			this.fieldType = fieldType;
			//2、解析 Printer<?> 中的 ？泛型类型，也就是 Printer<Date> 那么这里的 printerObjectType 就是 Date.class
			//打印对象类型 描述符
			this.printerObjectType = TypeDescriptor.valueOf(resolvePrinterObjectType(printer));
			//3、打印器
			this.printer = printer;
			//4、核心转换服务
			this.conversionService = conversionService;
		}

		@Override
		public Set<ConvertiblePair> getConvertibleTypes() {
			return Collections.singleton(new ConvertiblePair(this.fieldType, String.class));
		}

		/**
		 * 将对象转换为字符串
		 * @param source the source object to convert (may be {@code null}) //源对象
		 * @param sourceType the type descriptor of the field we are converting from
		 * @param targetType the type descriptor of the field we are converting to
		 * @return
		 */
		@Override
		@SuppressWarnings("unchecked")
		public Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
			if (!sourceType.isAssignableTo(this.printerObjectType)) {
				//如果源对象不是 Printer<?> 中的泛型类型，则进行转换
				source = this.conversionService.convert(source, sourceType, this.printerObjectType);
			}
			if (source == null) {
				return "";
			}
			//调用 Printer 的 print 方法，将对象转换为字符串并返回
			return this.printer.print(source, LocaleContextHolder.getLocale());
		}

		/**
		 * 获取 Printer 对象类型
		 * @param printer
		 * @return
		 */
		@Nullable
		private Class<?> resolvePrinterObjectType(Printer<?> printer) {
			//解析 Printer<?> 中的 ？泛型类型
			return GenericTypeResolver.resolveTypeArgument(printer.getClass(), Printer.class);
		}

		@Override
		public String toString() {
			return (this.fieldType.getName() + " -> " + String.class.getName() + " : " + this.printer);
		}
	}


	/**
	 * 解析转换器，把字符串转换为对象
	 */
	private static class ParserConverter implements GenericConverter {

		/**
		 * 1、目标类型
		 */
		private final Class<?> fieldType;

		/**
		 * 2、特定类型 解析器
		 */
		private final Parser<?> parser;

		/**
		 * 3、转换服务
		 */
		private final ConversionService conversionService;

		public ParserConverter(Class<?> fieldType, Parser<?> parser, ConversionService conversionService) {
			this.fieldType = fieldType;
			this.parser = parser;
			this.conversionService = conversionService;
		}

		@Override
		public Set<ConvertiblePair> getConvertibleTypes() {
			//String -> 目标类型
			return Collections.singleton(new ConvertiblePair(String.class, this.fieldType));
		}

		/**
		 * 转换
		 * @param source the source object to convert (may be {@code null}) //源对象
		 * @param sourceType the type descriptor of the field we are converting from //源对象类型描述
		 * @param targetType the type descriptor of the field we are converting to //目标对象类型描述
		 * @return
		 */
		@Override
		@Nullable
		public Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
			//1、直接将对象 转换为 String 类型，
			String text = (String) source;
			if (!StringUtils.hasText(text)) {
				return null;
			}
			Object result;
			try {
				//2、然后调用 Parser 的 parse 方法进行解析
				result = this.parser.parse(text, LocaleContextHolder.getLocale());
			}
			catch (IllegalArgumentException ex) {
				throw ex;
			}
			catch (Throwable ex) {
				throw new IllegalArgumentException("Parse attempt failed for value [" + text + "]", ex);
			}
			//3、如果解析后的对象不是目标类型，则再次使用转换进行转换，返回最终结果
			TypeDescriptor resultType = TypeDescriptor.valueOf(result.getClass());
			if (!resultType.isAssignableTo(targetType)) {
				result = this.conversionService.convert(result, resultType, targetType);
			}
			return result;
		}

		@Override
		public String toString() {
			return (String.class.getName() + " -> " + this.fieldType.getName() + ": " + this.parser);
		}
	}


	private class AnnotationPrinterConverter implements ConditionalGenericConverter {

		private final Class<? extends Annotation> annotationType;

		@SuppressWarnings("rawtypes")
		private final AnnotationFormatterFactory annotationFormatterFactory;

		private final Class<?> fieldType;

		public AnnotationPrinterConverter(Class<? extends Annotation> annotationType,
				AnnotationFormatterFactory<?> annotationFormatterFactory, Class<?> fieldType) {

			this.annotationType = annotationType;
			this.annotationFormatterFactory = annotationFormatterFactory;
			this.fieldType = fieldType;
		}

		@Override
		public Set<ConvertiblePair> getConvertibleTypes() {
			return Collections.singleton(new ConvertiblePair(this.fieldType, String.class));
		}

		@Override
		public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
			return sourceType.hasAnnotation(this.annotationType);
		}

		@Override
		@SuppressWarnings("unchecked")
		@Nullable
		public Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
			Annotation ann = sourceType.getAnnotation(this.annotationType);
			if (ann == null) {
				throw new IllegalStateException(
						"Expected [" + this.annotationType.getName() + "] to be present on " + sourceType);
			}
			AnnotationConverterKey converterKey = new AnnotationConverterKey(ann, sourceType.getObjectType());
			GenericConverter converter = cachedPrinters.get(converterKey);
			if (converter == null) {
				Printer<?> printer = this.annotationFormatterFactory.getPrinter(
						converterKey.getAnnotation(), converterKey.getFieldType());
				converter = new PrinterConverter(this.fieldType, printer, FormattingConversionService.this);
				cachedPrinters.put(converterKey, converter);
			}
			return converter.convert(source, sourceType, targetType);
		}

		@Override
		public String toString() {
			return ("@" + this.annotationType.getName() + " " + this.fieldType.getName() + " -> " +
					String.class.getName() + ": " + this.annotationFormatterFactory);
		}
	}


	private class AnnotationParserConverter implements ConditionalGenericConverter {

		private final Class<? extends Annotation> annotationType;

		@SuppressWarnings("rawtypes")
		private final AnnotationFormatterFactory annotationFormatterFactory;

		private final Class<?> fieldType;

		public AnnotationParserConverter(Class<? extends Annotation> annotationType,
				AnnotationFormatterFactory<?> annotationFormatterFactory, Class<?> fieldType) {

			this.annotationType = annotationType;
			this.annotationFormatterFactory = annotationFormatterFactory;
			this.fieldType = fieldType;
		}

		@Override
		public Set<ConvertiblePair> getConvertibleTypes() {
			return Collections.singleton(new ConvertiblePair(String.class, this.fieldType));
		}

		@Override
		public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
			return targetType.hasAnnotation(this.annotationType);
		}

		@Override
		@SuppressWarnings("unchecked")
		@Nullable
		public Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
			Annotation ann = targetType.getAnnotation(this.annotationType);
			if (ann == null) {
				throw new IllegalStateException(
						"Expected [" + this.annotationType.getName() + "] to be present on " + targetType);
			}
			AnnotationConverterKey converterKey = new AnnotationConverterKey(ann, targetType.getObjectType());
			GenericConverter converter = cachedParsers.get(converterKey);
			if (converter == null) {
				Parser<?> parser = this.annotationFormatterFactory.getParser(
						converterKey.getAnnotation(), converterKey.getFieldType());
				converter = new ParserConverter(this.fieldType, parser, FormattingConversionService.this);
				cachedParsers.put(converterKey, converter);
			}
			return converter.convert(source, sourceType, targetType);
		}

		@Override
		public String toString() {
			return (String.class.getName() + " -> @" + this.annotationType.getName() + " " +
					this.fieldType.getName() + ": " + this.annotationFormatterFactory);
		}
	}



	private static class AnnotationConverterKey {

		private final Annotation annotation;

		private final Class<?> fieldType;

		public AnnotationConverterKey(Annotation annotation, Class<?> fieldType) {
			this.annotation = annotation;
			this.fieldType = fieldType;
		}

		public Annotation getAnnotation() {
			return this.annotation;
		}

		public Class<?> getFieldType() {
			return this.fieldType;
		}

		@Override
		public boolean equals(@Nullable Object other) {
			return (this == other || (other instanceof AnnotationConverterKey that &&
					this.fieldType == that.fieldType && this.annotation.equals(that.annotation)));
		}

		@Override
		public int hashCode() {
			return (this.fieldType.hashCode() * 29 + this.annotation.hashCode());
		}
	}

}
